<?php
/**
 * Created by PhpStorm.
 * User: lihui
 * Date: 3/26/2021
 * Time: 16:36
 */
namespace app\common\model\coin;

use app\common\model\base\A3Mall;
use app\common\model\file\Association;
use app\common\model\order\OrderTake;

use app\common\model\system\SqlToRedis;
use app\common\model\users\UsersKey;
use app\common\traits\recordAllRun;
use mall\basic\Users;
use think\facade\Db;
use think\facade\Log;
use think\Model;

class CoinRule extends A3Mall
{
    use recordAllRun;   // 新增链表查询设置
    protected $autoWriteTimestamp = true;
    protected $name = "coin_rule";

    /**
     * 关联文件类型
     */
    const FILE_TYPE = 'coin_rule';

    protected $type = [
        "id"=>"string",
        "coin_id"=>"string",
        "user_id"=>"integer",
        "type"=>"integer",
        "first_amount"=>"float",
        "num"=>"integer",
        "stop_profit_ratio"=>"float",
        "stop_profit_callback"=>"float",
        "cover_callback"=>"float",
        "is_first_double"=>"integer",
        "is_open"=>"integer",
        "coin_rule_run_id"=>"integer",
        "create_time"=>"integer",
        "update_time"=>"integer",
    ];

    const STATUS_OPEN = 1;    //开启
    const STATUS_CLOSE = 2; // 关闭

    // 判断是否首单加倍
    const IS_FIRST_DOUBLE_TRUE = 1; //是
    const IS_FIRST_DOUBLE_FALSE = 2;    //否

    // 偏差量参数[也用于判断是否计算]
    const  DEVIATIONS_CALCULATION_TYPE = [
        'OPEN'  => 1,
        'CLOSE' => 2,
    ];

    // 买卖规则列表
    const TRADE_RULE_LIST = [   // 策略已关闭，或处于单次策略状态时，卖出类型自动以 常规交易方式计算（ROUTINE）
        'ROUTINE'   => 1,   // 常规交易方式:卖出时全盘卖出
        'SAVE_FIRST'    => 2,   // 保留首单金额: 当总金额 > 下次首单金额（含倍数判断） + 币种最小卖出金额 时进行首单金额全盘保存（即下次首单无需调用第三方接口）
        'SAVE_FIRST_MAX'    => 3,   // 最大限度预留首单金额：当 总金额 > 最小卖出金额 并且 总金额 > 最小买入金额 时执行进行首单金额预留；预留规则：
                /* 3 的规则计算
                 * a = 总金额
                 * b = 最小卖出金额
                 * c = 最小买入金额
                 * d = 下次买入金额
                 * save = 保留金额
                 * sell = 卖出金额
                 *
                 * 以下为计算
                 * sell = b
                 * save = a - sell
                 * 如果 save >= d 那么
                 *      save = d
                 *      sell = a - d
                 *      结束
                 * 如果 save < d 并且 save > d - c那么
                 *      save = d - c
                 *      sell = a - d
                 *      结束
                 * 结束
                 * */
    ];



    //状态类型
    private static $status_text_mapping=[
        self::STATUS_OPEN=>'开启',
        self::STATUS_CLOSE=>'关闭'
    ];

    const TYPE_ONE = 1;    //1策略循环
    const TYPE_TWO = 2; //2单次循环

    // 卖出类型
    const SELL_TYPE_LIST = [
        'MARKET'    => 1,   //市价卖
        'LIMIT'     => 2,   //限价卖
    ];

    //状态类型
    private static $type_text_mapping=[
        self::TYPE_ONE=>'策略循环',
        self::TYPE_TWO=>'单次循环'
    ];


    /** 获取关联所有数据的列表
     * @param array $whereTmp  条件
     * @param array $limit  查询数量
     * @param string $order 排序
     * @param array $fields 查询字段（暂缺）
     * @return array
     */
    public function recordAll($whereTmp = [],$limit = [],$orderTmp = [],$fieldsTmp= []) {

        $tableName =  static::getTable();
        return $this->recordAllRun([$whereTmp,$limit,$orderTmp,$fieldsTmp],[],[
            'coin'      => [Coin::getTable(),'coin.id=' . $tableName . '.coin_id','LEFT'],
            'coin_rule_run'      => [CoinRuleRun::getTable(),'coin_rule_run.id=' . $tableName . '.coin_rule_run_id','LEFT'],
            'user'      => [\app\common\model\users\Users::getTable(),'user.id=' . $tableName . '.user_id','INNER'],
            'users_key'      => [UsersKey::getTable(),'users_key.id=' . $tableName . '.user_id','INNER'],
            'last_run'      => [CoinRuleRun::getTable(),'last_run.id=' . $tableName . '.last_coin_rule_run_id','LEFT'],
        ]);
    }

    /**
     * 获取循环策略 1策略循环2单次循环
     */
    public static function fetchTypeList() {
        $list = [
            [
                'key'   => 1,
                'value' => '策略循环',
            ],
            [
                'key'   => 2,
                'value' => '单次循环',
            ]
        ];
        return $list;
    }

    /**
     * 判断是否首单加倍
     * @param $isFirstDouble
     */
    public static function isFirstDouble($isFirstDouble) {
        return $isFirstDouble == static::IS_FIRST_DOUBLE_TRUE?true:false;
    }

    /**
     * 用于判断是否计算偏差量
     * @param $deviationsCalculation
     * @return bool
     */
    public static function isDeviationsCalculation(int $deviationsCalculation) {
        return $deviationsCalculation == static::DEVIATIONS_CALCULATION_TYPE['OPEN']?true:false;
    }


    /**
     * 更新 执行中ID
     * @param int $id
     * @param int $runId
     */
    public function updateRunId(int $id,int $runId,$isOpen = null,int $lastRunId = null) {
        $updateData = [
            'coin_rule_run_id'  => $runId,
            'update_time'   => time(),
        ];
        if(!is_null($isOpen)) {
            $updateData['is_open'] = $isOpen;
        }
        $where = [
            ['id','=',$id],
        ];
        if($runId > 0) {
            $where[] = ['coin_rule_run_id','=',0];
        } else {
            if(!is_null($lastRunId)) {  // 判断是否插入上次执行ID
                $updateData['last_coin_rule_run_id'] = $lastRunId;
            }
            $where[] = ['coin_rule_run_id','>',0];
        }
        if(!$result = $this->where($where)->update($updateData)) {
            throw new \Exception('数据发生变化，请刷新重试');
        }
        return true;
    }


    /**
     * 获取详情
     * @param $id
     * @return array|null|\think\Model
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function findById($id,$userId = null) {
        $where = [
            'id'    => $id,
        ];
        if(!empty($userId)) {
            $where['user_id'] = $userId;
        }
        return $this->where($where)->find();
    }

    /**
     * 关联运行规则
     */
    public function coinRuleRun() {
        return $this->hasOne(CoinRuleRun::class,'coin_rule_run_id')->joinType("INNER");
    }

    /**
     * 关联补仓
     */
    public function coinRuleCover() {
        return $this->hasMany(CoinRuleCover::class,'coin_rule_id');
    }

    /**
     * 获取规则人
     * @return \think\model\relation\HasOne
     */
    public function user() {
        return $this->hasOne(\app\common\model\users\Users::class,"id","user_id")->joinType("INNER");

    }


    /**
     * 获取用户ID
     * @param null $userId 用户ID
     * @return int|mixed|null
     * @throws \Exception
     */
    private function getUserId($userId = null) {
        if(!$userId = is_integer($userId)?$userId:Users::get('id')) {
            throw new \Exception('获取用户ID信息失败');
        }
        return $userId;
    }


    //获取状态文本
    public static function getStatusText(int $status){
        return self::$status_text_mapping[$status] ?? '';
    }

    //获取状态文本
    public static function getTypeText(int $type){
        return self::$type_text_mapping[$type] ?? '';
    }

    public function getList($condition,$size=10,$page=1){
        $count = $this->withJoin(["coinRuleRun"])->where($condition)->count();
        $data = $this->withJoin(["coinRuleRun"])->where($condition)->order('coin_rule.id','DESC')->paginate($size);
        return [
            "count"=>$count,
            "data"=>array_map([$this,'format'],$data->items())
        ];
    }


    public function getStatusTextAttr($value,$data)
    {
        return static::getStatusText($data['status']) ?? '';
    }

    public function getPublishTimeTextAttr($value,$data)
    {
       return date("Y-m-d H:i:s",$data['publish_time']);
    }

    public function getExpTimeTextAttr($value,$data)
    {
       return date("Y-m-d H:i:s",$data['exp_time']);
    }

    public static function onBeforeInsert(Model $model)
    {
        try{

        }catch (\Exception $e){

            //return $this->returnAjax('系统内部错误'.$e->getMessage().$e->getLine(),0);
        }

    }

    public static function onAfterUpdate($user)
    {
        Log::log('test','后置更新');
        (new SqlToRedis())->getUserRule(1,1);
    }

    public static function onAfterInsert($user)
    {
        Log::log('test','后置插入');
        (new SqlToRedis())->getUserRule(1,1);
    }


    protected function format($value)
    {
//        $value->status_name=$value->status_text;
//        $value->type_name= $value->type_name ?? $value->job_type->name ?? $value->jobType__name;
//        $value->publisher=$value->username;
//        $value->publish_time_text=$value->publish_time_text;
//        $value->exp_time_text=$value->exp_time_text;
        return $value;
    }

}
